﻿using System.Collections.Generic;
using ExtremeSwank.Authentication.OpenID.Persistence;
using System.Collections.Specialized;

namespace ExtremeSwank.Authentication.OpenID
{
    /// <summary>
    /// State object passed among static methods that implement OpenID authentication steps.
    /// </summary>
    public class StateContainer
    {
        ProtocolVersion _AuthVersion;
        AuthenticationMode _AuthMode;
        KeyEncryption _Encryption;
        string _ClaimedIdentity;
        string _IdentityURL;
        string _Identity;
        string _TrustRoot;
        string _OpenIDServer;
        string _ReturnToURL;
        bool _UseDirectedIdentity;
        List<IExtension> _ExtensionPlugins;
        List<IDiscovery> _DiscoveryPlugins;
        IAssociationPersistence _AssociationManager;
        ISessionPersistence _SessionManager;
        NameValueCollection _RequestArguments;

        /// <summary>
        /// Creates a new StateContainer.
        /// </summary>
        public StateContainer()
        {
            _RequestArguments = new NameValueCollection();
            _ExtensionPlugins = new List<IExtension>();
            _DiscoveryPlugins = new List<IDiscovery>();
            _AssociationManager = new ApplicationAssociationManager();
            _SessionManager = new SessionSessionManager();
        }

        /// <summary>
        /// Creates a new StateContainer.
        /// </summary>
        /// <param name="assocmgr">Association persistence manager.</param>
        /// <param name="sessionmgr">Session persistence manager.</param>
        public StateContainer(IAssociationPersistence assocmgr, ISessionPersistence sessionmgr)
        {
            _RequestArguments = new NameValueCollection();
            _ExtensionPlugins = new List<IExtension>();
            _DiscoveryPlugins = new List<IDiscovery>();
            _AssociationManager = assocmgr;
            _SessionManager = sessionmgr;
        }

        /// <summary>
        /// Gets or sets the Association persistence manager.
        /// </summary>
        public IAssociationPersistence AssociationManager
        {
            get { return _AssociationManager; }
            set { _AssociationManager = value; }
        }

        /// <summary>
        /// Gets or sets the Session persistence manager.
        /// </summary>
        public ISessionPersistence SessionManager
        {
            get { return _SessionManager; }
            set { SessionManager = value; }
        }

        /// <summary>
        /// Gets or sets the version of the OpenID Protocol currently being used.
        /// </summary>
        public ProtocolVersion AuthVersion
        {
            get { return _AuthVersion; }
            set { _AuthVersion = value; }
        }

        /// <summary>
        /// Gets or sets the authentication mode currently being used.
        /// </summary>
        public AuthenticationMode AuthMode
        {
            get { return _AuthMode; }
            set { _AuthMode = value; }
        }

        /// <summary>
        /// Gets or sets the type of encryption used for shared key negotiation.
        /// </summary>
        public KeyEncryption Encryption
        {
            get { return _Encryption; }
            set { _Encryption = value; }
        }

        /// <summary>
        /// Gets or sets the identity claimed by the end user.
        /// </summary>
        public string ClaimedIdentity
        {
            get { return _ClaimedIdentity; }
            set { _ClaimedIdentity = value; }
        }

        /// <summary>
        /// Gets or sets the identity URL corresponding to the user's OpenID.  Use Utility.UpdateIdentity() to set this value.
        /// </summary>
        public string IdentityURL
        {
            get { return _IdentityURL; }
            set { _IdentityURL = value; }
        }

        /// <summary>
        /// Gets or sets a normalized string representing the user's OpenID.  Use Utility.UpdateIdentity() to set this value.
        /// </summary>
        public string Identity
        {
            get { return _Identity; }
            set { _Identity = value; }
        }

        /// <summary>
        /// Gets or sets the root URL of this web server or domain.
        /// </summary>
        public string TrustRoot
        {
            get { return _TrustRoot; }
            set { _TrustRoot = value; }
        }

        /// <summary>
        /// Gets or sets the OpenID Server URL.
        /// </summary>
        public string OpenIDServer
        {
            get { return _OpenIDServer; }
            set { _OpenIDServer = value; }
        }
        /// <summary>
        /// Persists the cnonce value in the current session so it can
        /// be verified when the authentication response is received.
        /// </summary>
        /// <remarks>
        /// This is extremely important to ensure that simply replaying
        /// the authentication response does not result in successful
        /// authentication.  AllowLogin is not populated until an authentication
        /// request has been triggered, and it is cleared as soon as the 
        /// matching request is received and verified.
        /// </remarks>
        public int Cnonce
        {
            get
            {
                return _SessionManager.Cnonce;
            }
            set { _SessionManager.Cnonce = value; }
        }

        /// <summary>
        /// Gets or sets the URL which the User Agent will be returned to.
        /// </summary>
        public string ReturnToURL
        {
            get { return _ReturnToURL; }
            set { _ReturnToURL = value; }
        }

        /// <summary>
        /// Whether or not a delegate ID was discovered.
        /// If set to false, the identity received from the provider should be
        /// the recorded identity.  If set to true, the initial identity should
        /// be used.
        /// </summary>
        public bool DiscoveredDelegate
        {
            get
            {
                return _SessionManager.DiscoveredDelegate;
            }
            set { _SessionManager.DiscoveredDelegate = value; }
        }

        /// <summary>
        /// Set to true to enforce use of Directed Identity.
        /// </summary>
        public bool UseDirectedIdentity
        {
            get { return _UseDirectedIdentity; }
            set 
            {
                if (value == true)
                {
                    ClaimedIdentity = OpenIDNamespaces.IdentifierSelect;
                    IdentityURL = OpenIDNamespaces.IdentifierSelect;
                    Identity = OpenIDNamespaces.IdentifierSelect;
                    AuthVersion = ProtocolVersion.V2_0;
                }
                _UseDirectedIdentity = value; 
            }
        }

        /// <summary>
        /// Gets or sets the list of Extension Plugins registered into the current state.
        /// </summary>
        public IExtension[] ExtensionPlugins
        {
            get
            {
                return _ExtensionPlugins.ToArray();
            }
        }
        /// <summary>
        /// Gets or sets the list of Discovery Plugins registered into the current state.
        /// </summary>
        public IDiscovery[] DiscoveryPlugins
        {
            get
            {
                return _DiscoveryPlugins.ToArray();
            }
        }

        /// <summary>
        /// Gets or sets the request arguments.
        /// </summary>
        public NameValueCollection RequestArguments
        {
            get
            {
                return _RequestArguments;
            }
            set
            {
                _RequestArguments = value;
            }
        }

        /// <summary>
        /// Registers a discovery plugin.
        /// </summary>
        /// <param name="plugin">IDiscovery object to register.</param>
        public void RegisterPlugin(IDiscovery plugin)
        {
            for (int i = 0; i < _DiscoveryPlugins.Count; i++)
            {
                if (_DiscoveryPlugins[i].GetType() == plugin.GetType())
                {
                    _DiscoveryPlugins[i] = plugin;
                    return;
                }
            }
            _DiscoveryPlugins.Add(plugin);
        }
        /// <summary>
        /// Registers an extension plugin.
        /// </summary>
        /// <param name="plugin">IExtension object to register.</param>
        public void RegisterPlugin(IExtension plugin)
        {
            for (int i = 0; i < _ExtensionPlugins.Count; i++)
            {
                if (_ExtensionPlugins[i].GetType() == plugin.GetType())
                {
                    _ExtensionPlugins[i] = plugin;
                    return;
                }
            }
            _ExtensionPlugins.Add(plugin);
        }
    }
}
